{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Grove Ultrasonic Ranger Example\n",
    "\n",
    "This example shows how to use the \n",
    "[Grove ultrasonic_ranger](https://www.seeedstudio.com/Grove---Ultrasonic-Ranger-p-960.html)\n",
    "on the board. The Ultrasonic sensor has a maximal range of 400 cm, \n",
    "a minimal range of 3 cm and resolution of 1 cm.\n",
    "\n",
    "If no obstacle is seen by the sensor, 500 cm will be returned \n",
    "by default.\n",
    "\n",
    "In this notebook, we only show how to control the grove ultrasonic ranger \n",
    "using the Pmod interface. A PYNQ Pmod Grove adapter is required in this \n",
    "notebook. With proper changes in this notebook, users can also control \n",
    "the ultrasonic ranger using the Arduino shield."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "\n",
       "require(['notebook/js/codecell'], function(codecell) {\n",
       "  codecell.CodeCell.options_default.highlight_modes[\n",
       "      'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n",
       "  Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
       "      Jupyter.notebook.get_cells().map(function(cell){\n",
       "          if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
       "  });\n",
       "});\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from pynq.overlays.base import BaseOverlay\n",
    "\n",
    "base = BaseOverlay(\"base.bit\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Use Microblaze to control the ultrasonic ranger\n",
    "The following program assumes that the ultrasonic sensor is attached to the \n",
    "G1 connector of the Pmod Grove adapter, and the adapter is connected to PMODA.\n",
    "\n",
    "For the timer controller, the control registers are shown as follows:\n",
    "\n",
    "|Register name |Register functionality               |Register value |\n",
    "|:-------------|:------------------------------------|:----------------|\n",
    "|TCSR0         |Timer 0 Control and Status Register  |0x00             |\n",
    "|TLR0          |Timer 0 Load Register                |0x04             |\n",
    "|TCR0          |Timer 0 Counter Register             |0x08             |\n",
    "|TCSR1         |Timer 1 Control and Status Register  |0x10             |\n",
    "|TLR1          |Timer 1 Load Register                |0x14             |\n",
    "|TCR1          |Timer 1 Counter Register             |0x18             |\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "%%microblaze base.PMODA\n",
    "\n",
    "#include \"xparameters.h\"\n",
    "#include \"xtmrctr.h\"\n",
    "#include \"gpio.h\"\n",
    "#include \"timer.h\"\n",
    "#include <pmod_grove.h>\n",
    "\n",
    "#define TCSR0 0x00\n",
    "#define TLR0 0x04\n",
    "#define TCR0 0x08\n",
    "#define TCSR1 0x10\n",
    "#define TLR1 0x14\n",
    "#define TCR1 0x18\n",
    "#define MAX_COUNT 0xFFFFFFFF\n",
    "\n",
    "void create_10us_pulse(gpio usranger){\n",
    "    gpio_set_direction(usranger, GPIO_OUT);\n",
    "    gpio_write(usranger, 0);\n",
    "    delay_us(2);\n",
    "    gpio_write(usranger, 1);\n",
    "    delay_us(10);\n",
    "    gpio_write(usranger, 0);\n",
    "}\n",
    "\n",
    "void configure_as_input(gpio usranger){\n",
    "    gpio_set_direction(usranger, GPIO_IN);\n",
    "}\n",
    "\n",
    "unsigned int capture_duration(gpio usranger){\n",
    "    unsigned int count1, count2;\n",
    "    count1=0;\n",
    "    count2=0;\n",
    "    XTmrCtr_WriteReg(XPAR_TMRCTR_0_BASEADDR, 0, TLR0, 0x0);\n",
    "    XTmrCtr_WriteReg(XPAR_TMRCTR_0_BASEADDR, 0, TCSR0, 0x190);\n",
    "    while(!gpio_read(usranger));\n",
    "    count1=XTmrCtr_ReadReg(XPAR_TMRCTR_0_BASEADDR, 0, TCR0);\n",
    "    while(gpio_read(usranger));\n",
    "    count2=XTmrCtr_ReadReg(XPAR_TMRCTR_0_BASEADDR, 0, TCR0);\n",
    "    if(count2 > count1) {\n",
    "        return (count2 - count1);\n",
    "    } else {\n",
    "        return((MAX_COUNT - count1) + count2);  \n",
    "    }\n",
    "}\n",
    "\n",
    "unsigned int read_raw(){\n",
    "    gpio usranger;\n",
    "    usranger = gpio_open(PMOD_G1_A);\n",
    "    create_10us_pulse(usranger);\n",
    "    configure_as_input(usranger);\n",
    "    return capture_duration(usranger);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Do one-time distance measurement\n",
    "Remember to put some obstacles in front of the sensor, otherwise the default\n",
    "value will be returned."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pynq import Clocks\n",
    "\n",
    "def read_distance_cm():\n",
    "    raw_value = read_raw()\n",
    "    clk_period_ns = int(1000 / Clocks.fclk0_mhz)\n",
    "    num_microseconds = raw_value * clk_period_ns * 0.001\n",
    "    if num_microseconds * 0.001 > 30:\n",
    "        return 500\n",
    "    else:\n",
    "        return num_microseconds/58"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "11.873448275862069"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "read_distance_cm()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
